package net.xumf.support.aop;

import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import net.xumf.support.annotation.UserActionLog;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import io.swagger.annotations.ApiOperation;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;

@Slf4j
public class LogMonitor {

    @Pointcut("@annotation(net.xumf.support.annotation.UserActionLog) || @annotation(io.swagger.annotations.ApiOperation)")
    public void pointcut() {
    }

    @After("pointcut()")
    public void doAfter(JoinPoint joinPoint) {
        try {
            this.logEvent(joinPoint, "INFO", "");
        } catch (final Exception e) {
            log.error("异常信息:{}", e);
        }
    }

    @AfterThrowing(pointcut = "pointcut()", throwing = "e")
    public void doAfterThrowing(JoinPoint joinPoint, Throwable e) {
        try {
            this.logEvent(joinPoint, "ERROR", ExceptionUtils.getStackTrace(e));
        } catch (final Exception ex) {
            log.error("异常信息:{}", ex.getMessage());
        }
    }

    protected void logEvent(JoinPoint joinPoint, String level, String message) {
        try {
            final EventParameter eventParameter = this.getEventParameter(joinPoint, level, message);
            this.logEvent(eventParameter);
        } catch (final Exception e) {
            log.error("记录系统事件出错", e);
        }
    }

    protected void logEvent(final EventParameter eventParameter) {
        final HttpServletRequest req = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes())
                .getRequest();
        eventParameter.setUrl(req.getRequestURL().toString());
        log.info("UserActionLog:{}", eventParameter.toString());
    }

    private EventParameter getEventParameter(JoinPoint joinPoint, String level, String message) throws Exception {
        // 新建日志事件对象
        final EventParameter eventParameter = new EventParameter();
        final String targetName = joinPoint.getTarget().getClass().getName();
        final String methodName = joinPoint.getSignature().getName();
        final Object[] arguments = joinPoint.getArgs();
        final Class targetClass = Class.forName(targetName);
        final Method[] methods = targetClass.getMethods();

        eventParameter.setSource(targetName + ":" + methodName);
        eventParameter.setLevel(level);
        eventParameter.setMessage(message);
        for (final Method method : methods) {
            if (method.getName().equals(methodName)) {
                final Class[] methodParameterTypes = method.getParameterTypes();
                if (methodParameterTypes.length == arguments.length) {

                    ApiOperation apiOperation = method.getAnnotation(ApiOperation.class);
                    if (null == apiOperation) {
                        eventParameter.setName(method.getAnnotation(UserActionLog.class).name());
                        eventParameter.setDesc(method.getAnnotation(UserActionLog.class).desc());
                    } else {
                        eventParameter.setName(apiOperation.value());
                        eventParameter.setDesc(apiOperation.notes());
                    }
                    eventParameter.setArguments(StringUtils.join(arguments, ","));
                    break;
                }
            }
        }
        return eventParameter;
    }

    /**
     * 事件参数类
     */
    @Data
    protected static class EventParameter {

        // 事件来源
        private String source;
        // 事件级别
        private String level;
        // 事件名称
        private String name;
        // 事件说明
        private String desc;
        // 事件调用方法参数
        private String arguments;
        // 事件信息
        private String message;
        // 事件请求url
        private String url;
    }
}
